การ Authentication ใน FastAPI ด้วย Firebase Admin SDK
สวัสดีครับ เหล่านักพัฒนาผู้รักความสะดวก!
เบื่อไหมครับกับการเขียนโค้ด Authentication ซ้ำๆ วนไปวนมา อยากจะมีระบบที่ใช้งานง่าย ปลอดภัย และรวดเร็วทันใจ ⚡️ ถ้าใช่! บทความนี้เหมาะกับคุณมาก
วันนี้ผมจะมาแนะนำวิธีการใช้ Firebase Admin กับ FastAPI สองยักษ์ใหญ่ที่จะช่วยให้คุณสร้างระบบ Authentication สุดเจ๋งได้แบบง่ายๆ โดยไม่ต้องเสียเวลาเขียนโค้ดเยอะแยะ บทความนี้แทบจะละเอียดมาก ไม่เหมาะกับผู้ขี้เกลียดอ่านอะไรเยอะแยะ 😁 เพราะงั้น ขั้นตอนไหนคุณรู้อยู่แล้วข้ามไปได้เลย 🧐
เตรียมตัวให้พร้อม แล้วไปลุยกันเลย!
ขอบเขตเครื่องมือ
Tool | Version | Link |
---|---|---|
Python | 3.11.5 | https://www.python.org/downloads/release/python-3115/ |
Visual Studio | current | https://code.visualstudio.com/ |
Postman | current | https://www.postman.com/ |
OS | Windows 11 |
เริ่มต้นใช้งาน Firebase Admin
- เข้าใช้งาน Firebase ก่อน
- ไปที่ https://console.firebase.google.com/u/0/ และ เลือก Add project เพื่อสร้าง project ก่อน
- ตั้งชื่อได้อิสระได้เลย เสร็จแล้วก็กด Continue โล้ดดดด
- จะเป็นการถามว่าจะใช้งาน Google Analytics หรือเปล่า ถ้าไม่ก็ติ๊กตรง Enable… มันก็จะปิด แต่ว่าระบบแนะนำให้เปิด ก็เปิดแล้วกัน จากนั้นกด Continue ได้เลย แต่กรณีคุณปิดมันจะเป็นปุ่ม Create project
- หากคุณเปิดเพราะว่าเชื ่อบทความ (^_^) ทำการเลือกบัญชีได้เลย แต่หากไม่เคยใช้ Google Analytics อาจจะต้องสร้างบัญชีก่อนนะครับถึงจะมีให้เลือก แต่ถ้าไม่อยากวุ่นกดที่ Previous เพื่อย้อนกลับไปปิดก็ได้เช่นกัน เพื่อทุกอย่างเรียบร้อยกด Create project ได้เลย
- จากนั้นก็รอสักระยะ
- เสร็จแล้ว!!! กด Continue ได้เลย!
- เมื่อมาถึงหน้านี้หากท่านยังไม่เคยใช้งานมาก่อน ตั้งสติและใจเย็นๆ 😮💨😮💨 บทความนี้เราจะใช้แค่ Authentication เท่านั้น! 🧐
- แทบเมนูด้านซ้ายเลือก Build และ **Authentication **ที่เราหมายปอง
- เช่นเดิมหน้านี้ก็ใจเย็นๆ จุดหมายเราคือปุ่ม Get stated แต่ถ้าคุณอยากรู้อะไรเพิ่มเติมก็ดูคลิปที่เขานำมาให้รอก็ได้ ถ้าไม่เก่งอังกฤษ (เหมือนผม 😅) ก็เปิดคำบรรยายแล้วแปลอัตโนมัติเป็นไทย ก็ได้เช่นกัน ถ้าพร้อมแล้วค่อยมากด Get stated
- เหล่านี้คือเหล่า providers ที่เราสามารถใช้ในการ **Authentication **ได้แต่สิ่งที่เราสนใจแบบง่ายๆพื้นๆคือ **Email/Password **ส่วนอื่นๆไม่ต้องไปสนใจ! เพราะผมยังใช้ไม่เป็น 😎เพราะงั้นเลือก **Email/Password **ได้เลย
- ทำการ Enable ที่ **Email/Password **แล้วเลือก Saveได้เลย ส่วน Email link (passwordless sign-in) ยังไม่ได้ลองเช่นกันครับผม เพราะงั้นช่างมัน 😅
- ถ้าได้แบบนี้เป็นการเสร็จในส่วนนี้แล้ว
ต่อไปเราจะไปเตรียมโฟลเดอร์และไฟล์
จัดการโฟลเดอร์และไฟล์ให้เตรียมพร้อมใช้งาน
- สร้างโฟลเดอร์
- เพื่อไว้เก็บทุกอย่างที่เราจะทำ ส่วนนี้จะเป็นส่วนที่ยากที่สุดเพราะไม่รู้ว่าจะใช้ชื่อว่าอะไรดี 😅 จริงๆจะใช้ชื่อว่าอะไรก็ได้ แต่ทางผมของตั้งชื่อว่า **fastapi-firebase-auth **แล้วกันจากนั้นเข้าไปในโฟลเดอร์ให้เรียบร้อย
- เราจะทำการนำโฟลเดอร์ที่เราสร้างไปใช้งานใน **Visual Studio Code **หากคุณใช้ windows คุณสามารถทำตามนี้ได้เลย (หากคุณใช้อย่างอื่นคุณต้องค่อยๆ cd ไปยังโฟลเดอร์ของคุณและใช้ code . ได้เหมือนกัน)
- เนื่องจากว่าคุณจำเป็นต้องติดตั้ง firebase_admin และ fastapi ดังนั้นเราจึงต้องสร้าง virtual environment ขึ้นด้วยด้วยคำสั่ง
python -m venv .env
- โดย .env คือชื่อจะใช้ชื่ออะไรก็ แต่แนะนำว่าใช้ชื่อนี้ง่ายกว่า
- เมื่อทุกอย่างเรียบร้อยจะเป็นแบบนี้จากนั้นทำการเปิดใช้งานด้วยคำสั่ง
.env/Scripts/Activate.ps1
-
หากคุณใช้ Terminal คือ Power Shell (ตรงนี้หากไม่ได้ลองศึกษาเรื่อง virtual environment python เพิ่มเติมนะครับ)
-
แต่หากไม่ได้จริงๆ ก็ไม่เป็นไร สำหรับมือใหม่มันอาจจะยุ่งยาก ทำขั้นต่อไปกันได้เลย!
ติดตั้งสิ่งต่างที่ต้องใช้กัน
- ติดตั้ง firebase-admin กัน https://pypi.org/project/firebase-admin/
pip install firebase-admin
- ติดตั้ง FastAPI กัน https://pypi.org/project/fastapi/ เพื่อใช้เป็น web framework
pip install fastapi
- ติดตั้ง Uvicorn กัน https://pypi.org/project/uvicorn/ เพื่อใช้เป็นตัว run server
pip install uvicorn
ส่วนนี้เสร็จเรียบร้อยต่อไปเราจะกลับไปที่ firebase เพื่อเอาอะไรบางอย่างจากมัน 🧐
ไปเอา Certificate จาก Firebase
- จากที่หน้าที่เราทำกันล่าสุดที่ firebase ทำการที่รูป ⚙️ หรือรูปเฟือง และเลือก Project settings
- จากนั้นไปที่แท็ป Service Accounts
- เลือกเป็น Python ก่อนจากนั้นก็ Generate new private key
- เลือก generate key
- คุณจะได้ไฟล์ .json มาจากนั้นนำมันมาใส่ไว้ในโฟลเดอร์ที่เราเตรียมก่อนหน้านี้
- เปลี่ยนชื่อใหม่ให้มันเพื่อความใช้งานง่าย จริงจะใช้ชื่อว่าอะไรก็ได้ แต่ส่วนนี้แนะนำแบบ 100% ให้ใช้ชื่อ **service-account.json **แบบนี้ดีกว่า ระวังด้วยหากคุณจะเปลี่ยนชื่อที่โฟลเดอร์เลย ไม่ต้องใช้ .json แต่ถ้าคุณเปลี่ยนใน vscode ต้องใช้ .json ด้วย ไม่งั้นจะเป็นลักษณะนี้
แบบที่ถูกคือ ✅
ข้อมูลในนี้สำคัญหากคุณจะนำไปใช้ต่อระวังอย่าให้หลุด แต ่ถ้าหลุดก็ไปลบแล้วเริ่มใหม่ 😅
เรียบร้อย เราจะไปส่วนต่อไปกัน
ลุยโค้ดกันได้เลย !
-
สร้างไฟล์ app.py ขึ้นมา
-
จากนั้นเราจะมาเริ่มใช้งาน FastAPI กันโดยคัดลอกโค้ดด้านล่างไปใส่ที่ app.py ได้เลย
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def index():
return {"message": "Hello World"}
- จากนั้น run server ด้วย uvicorn แล้วลอง test ด้วย Postman ดู
uvicorn app:app --reload
- app แรกคือชื่อไฟล์ app.py และ app ต่อไปคือชื่อตัว app = FastAPI() และ reload คือ เมื่อคุณแก้ไขโค้ ดและบันทึกมันจะรีโหลด web server ให้อัตโนมัติโดยไม่ต้องมาหยุดแล้วรันใหม่ โอเคนะ 😎
- สร้าง **init_firebase.py **เพื่อจะใช้เป็นเชื่อมต่อกับ firebase ด้วยไฟล์ service-account.json ที่เราได้มา โค้ดดังนี้
from firebase_admin import credentials, initialize_app
cred = credentials.Certificate("service-account.json")
initialize_app(cred)
print("firebase init done")
- กลับไปแก้ไขโค้ด app.py เพิ่มเติม
from init_firebase import *
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def index():
return {"message": "Hello World"}
- แล้วบันทึกรอรีโหลดหากมีข้อความ **firebase init done **ก็ถือเรียบร้อย 😎
- สร้างไฟล์ชื่อว่า models.pyเราจะทำการกำหนดรูปแบบ payload ที่เราต้องการนำเข้ามาซึ่งเราจะทำแบบง่ายๆจะรับเข้ามาแค่ email กับ password แบบนี้
from pydantic import BaseModel
class User(BaseModel):
email: str
password: str
- เราได้รูปแบบ payload แล้วเราจะกลับไปที่ app.py เพื่อทำการสร้างเส้น api ใหม่คือเส้น /signup เพื่อใช้ในการสมัครสมาชิกนั้นเองโดยจะใช้ method เพื่อจะรับ payload เข้ามา และเหมือนเดิมคัดลอกไปวางทับโค้ดเดิมใน app.py ได้เลยอย่าลืมบันทึุกด้วย 😎
from init_firebase import *
from fastapi import FastAPI
from models import User
app = FastAPI()
@app.get("/")
def index():
return {"message": "Hello World"}
@app.post("/signup")
def sign_up(user: User):
return user
- เทสใน Postman ได้เลยโดย payload เอาไปใส่ที่ตรง raw ได้เลยแล้วกด **Send **จะได้แบบรูปแบบ
{
"email":"wk23@gmail.com",
"password":"213sad2w2"
}
- ต่อเราจะทำการโค้ดในส่วนของการสมัครสมาชิกลงใน firebase กันจากโค้ดนี้ไปใส่ที่ app.py แล้วลองทดสอบเหมือนก่อนหน้าอีกครั้ง
from init_firebase import *
from fastapi import FastAPI
from models import User
from firebase_admin import auth
app = FastAPI()
@app.get("/")
def index():
return {"message": "Hello World"}
@app.post("/signup")
def sign_up(user: User):
email = user.email
password = user.password
user = auth.create_user(email=email, password=password)
print(user)
return user
11 . ลองกลับไปที่ firebase ส่วนของ Authentication คุณจะเห็นว่ามี email ตัวอย่างของเราโผล่ที่นั่นแล้ว!!
- แต่หากกลับไป Postman เราได้ข้อมูลอะไรมาเยอะแยะเลย เราจะมาดูส่วนที่สำคัญหน่อยแล้วกัน
{
"_data": {
"localId": "l6fNK2I2RwOft7FXV1ACpEEgX3e2",
"email": "wk23@gmail.com",
"passwordHash": "UkVEQUNURUQ=",
"emailVerified": false,
"passwordUpdatedAt": 1708104981299,
"providerUserInfo": [
{
"providerId": "password",
"federatedId": "wk23@gmail.com",
"email": "wk23@gmail.com",
"rawId": "wk23@gmail.com"
}
],
"validSince": "1708104981",
"disabled": false,
"createdAt": "1708104981299"
}
}
-
localId หรือ user_id เป็นรหัสสุ่มขึ้นมาไม่ซ้ำหรือใช้มันเป็น primary key ได้นั่นเอง
-
emailVerified เป็นสถานะที่บอกว่าอีเมลที่สมัครสมาชิกเข้ามานี้ทำการยืนยันอีเมลแล้วหรือยัง
- ต่อเราจะทำการสร้างเส้น /signin กันหรือเส้นที่จะใช้ในการเข้าสู่ระบบกัน แต่ว่าโชคร้ายที่ firebase_admin นี้ไม่มีการ sign in ด้วย email และ password 😭 แต่อย่าเศร้ามีวิธีแก้ 😎
- ทำการติดตั้ง requests https://pypi.org/project/requests/ เพิ่มเติม อย่าลืมหยุด web server โดยไปที่ Termibal การกด Ctrl + C
pip install requests
- และ python-dotenv https://pypi.org/project/python-dotenv/
pip install python-dotenv
- กลับไปที่ firebase เลือก Project settings
- คัดลอกข้อมูลที่อยู่หลัง Web API Key ไว้
- สร้างไฟล์ _env เฉยๆ ไม่มีนามสกุลไฟล์นะ ไว้เก็บข้อมูลสำคัญอย่าง Web API Key จากนั้นวางลงแบบนี้ อย่าลืมเปลี่ยน key นะของใครของมันถ้าไม่เชื่อ error ไม่รู้น๊า
FIREBASE_WEB_API_KEY=AIzaSyBQ8J_XYglnULI1U8vHnPYD-uz1TEjrxZY
- สร้างไฟล์หน่อยแล้วกันเวลาเรียก FIREBASE_WEB_API_KEY จะได้เรียกง่ายๆชื่อว่า settings.py แล้วใส่โค้ดนี้ลงไปได้เลย
import dotenv
import os
dotenv.load_dotenv("_env")
FIREBASE_WEB_API_KEY = os.environ.get("FIREBASE_WEB_API_KEY")
- แล้วทำการสร้างไฟล์ rest_firebase_api.py เราจะทำการใช้ของที่ firebase_admin ของ python ที่ไม่ได้ใส่มาใช้กัน คัดลอกโค้ดไปได้เลย
from settings import FIREBASE_WEB_API_KEY
import requests
import json
SIGN_IN_WITH_PASSWORD_URL = (
"https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword"
)
def sign_in_with_email_and_password(
email: str, password: str, return_secure_token: bool = True
):
payload = json.dumps(
{"email": email, "password": password, "returnSecureToken": return_secure_token}
)
r = requests.post(
SIGN_IN_WITH_PASSWORD_URL, params={"key": FIREBASE_WEB_API_KEY}, data=payload
)
return r.json()
- ต่อกันยังไม่จบในส่วนนี้เราได้ทำการสร้างฟังก์ที่ไว้ใช้ sign in เรียบร้อยแล้ว ต่อเราจะไปเขียนโค้ดในส่วน app.py กันต่อเหมือนเช่นเดิม คัดลอกไปเลย
from init_firebase import *
from fastapi import FastAPI
from models import User
from firebase_admin import auth
from rest_firebase_api import sign_in_with_email_and_password
app = FastAPI()
@app.get("/")
def index():
return {"message": "Hello World"}
@app.post("/signup")
def sign_up(user: User):
email = user.email
password = user.password
user = auth.create_user(email=email, password=password)
return user
@app.post("/signin")
def sign_in(user: User):
email = user.email
password = user.password
user = sign_in_with_email_and_password(email, password)
return user
- บันทึกแล้วรันด้วยคำสั่งเดิมและเทสใน Postman ได้เลยถ้าขึ้นแบบข้างล่างแสดงว่าสำเร็จ หากไม่สำเร็จเช็ค email กับ password ใน payload ใหม่ด ูน๊า
- ทีนี้ลองเทสกรณีรหัสผิดหรืออีเมลผิดดู
{
"email":"pg23@gmail.com",
"password":"213sad2w2"
}
กับ
{
"email":"wk23@gmail.com",
"password":"sdsddsd"
}
{
"email":"wk23@gmail.com",
"password":"213sad2w2"
}
- ถือว่าเยี่ยมเลยเราทำระบบ login เสร็จเรียบร้อยแล้วต่อไปเราจะให้มัน response ข้อมูล email_verified เข้ามาด้วยเพราะเราอยากรู้สถานะของผู้ใช้รายนี้ว่ายืนยันอีเมลหรือยังเราอาจจะเอาสถานะนี้ไปจำกัดสิทธิ์อะไรในอนาคตได้ เริ่มกันเหมือนเดิมคัดลอกไปได้เลย เราจะแก้ไขโค้ดในส่วน *def sign_in(user: User): *นี้เท่านั้นเพราะงั้นจะเอามาแค่ส่วนนี้จะได้ประหยัดพื้นที่นะ ที่ไฟล์ **app.py **เหมือนเช่นเคย
@app.post("/signin")
def sign_in(user: User):
email = user.email
password = user.password
user = sign_in_with_email_and_password(email, password)
email_verified = auth.get_user_by_email(email).email_verified
user["email_verified"] = email_verified
return user
- ลองเทสบน Postman ได้เลย ก็จะพบมาสถานะยังไม่ได้ยืนยันอีเมล
- และเส้นต่อไปแน่น่อนว่าต้องเป็นเส้น /send-email-verify โดยใช้เป็น method get หากมายิงเส้นส่งอีเมลไปหาอีเมลของผู้ใช้ที่มาการสมัครสมาชิกเข้ามา แต่ว่าการจะใช้เส้นนี้ได้จำเป็นต้องมี idToken สิ่งจะมาจาก เส้น /signin
โดยจะมี 2 ท่าให้เล่นคือ
-
- เส้น /send-email-verify ทำการรับ email และ password เข้ามาและเรียกฟังก์ชัน sign_in_with_email_and_password(email, password) เหมือน เส้น /signin วิธีนี้ไม่จำเป็นต้อง sign-in ก็ verify email ได้
-
- เส้น /signin หลังจากยิงเส้นนี้แล้วให้ทำการบันทึก idToken เป็น cookie เอาไว้ แล้วเส้น /send-email-verify ไม่จำเป็นต้องรับอะไรเข้าไป ไปดึง cookie idToken ที่บันทึกมาใช้ได้เลย วิธีนี้เลยจำเป็นต้อง sign-in ก่อนถึงจะ verify ได้
แน่นอนว่าเราเป็นคนคูลๆแบบสองมันดูยากกว่าแบบแรก เพราะงั้นเราเลือกวิธีที่ 2 😅 เพราะมันดูสมเหตุสมผลกว่าแค่นั้นเอง งั้นมาเริ่มโค้ดกันเหมือนเดิมเราจะไปแก้ที่ เส้น /signin โค้ดด้านล่างก็เอาไปใส่ที่ app.py
from datetime import datetime, timedelta
from init_firebase import *
from fastapi import FastAPI, Response
from models import User
from firebase_admin import auth
from rest_firebase_api import sign_in_with_email_and_password
app = FastAPI()
@app.get("/")
def index():
return {"message": "Hello World"}
@app.post("/signup")
def sign_up(user: User):
email = user.email
password = user.password
user = auth.create_user(email=email, password=password)
return user
@app.post("/signin")
def sign_in(user: User, response: Response):
email = user.email
password = user.password
user = sign_in_with_email_and_password(email, password)
email_verified = auth.get_user_by_email(email).email_verified
user["email_verified"] = email_verified
if "idToken" in user:
response.set_cookie(
key="id_token",
value=user["idToken"],
expires=int((datetime.utcnow() + timedelta(hours=1)).timestamp()),
)
return user
โดยจะดักด้วยว่าถ้าผู้ใช้งานกรอกอีเมลหรือรหั สผ่านไม่ถูกจะไม่ทำการ set_cookie
— key จะเปลี่ยนเป็นชื่ออื่นได้ — value คือข้อมูล idToken ที่อยู่ใน user — expires=datetime.utcnow() + datetime.timedelta(hours=1) cookie นี้จะถูกลบหรือหมดอายุไปในอีก 1 ชั่วโมง สามารถเปลี่ยนได้เช่น days=1 หรือ minutes=1 หรือ seconds=10 และแปลงเป็น timestamp อีกที
- บันทึกแล้วทดสอบได้เลย แล้วไปที่แท็ป Cookies
- ก่อนที่เราจะไปสร้างเส้น /send-email-verify กันต่อคงต้องหยุดก่อนสหาย เพราะว่าเสียใจด้วย firebase_admin ก็ไม่มี send-email-verify ให้เราใช้อีกแล้วครับท่าน 😭 ตั้งนั้นเราต้องใช้ท่าวรยุทธ์ rest เหมือนการ sign in เลย เพราะงั้นกลับไปที่ rest_firebase_api.py แล้วใส่โค้ดนี้ลงไปได้เลย
from settings import FIREBASE_WEB_API_KEY
import requests
import json
SIGN_IN_WITH_PASSWORD_URL = (
"https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword"
)
SEND_VERIFY_EMAIL_URL = "https://identitytoolkit.googleapis.com/v1/accounts:sendOobCode"
def sign_in_with_email_and_password(
email: str, password: str, return_secure_token: bool = True
):
payload = json.dumps(
{"email": email, "password": password, "returnSecureToken": return_secure_token}
)
r = requests.post(
SIGN_IN_WITH_PASSWORD_URL, params={"key": FIREBASE_WEB_API_KEY}, data=payload
)
return r.json()
def send_verify_email(id_token):
headers = {"Content-Type": "application/json"}
url = f"{SEND_VERIFY_EMAIL_URL}?key={FIREBASE_WEB_API_KEY}"
data = {"requestType": "VERIFY_EMAIL", "idToken": id_token}
response = requests.post(
url, headers=headers, json=data, auth=("api_key", FIREBASE_WEB_API_KEY)
)
return response
- เรียบร้อยพร้อมใช้งานที่นี้ไปสร้างเส้น /send-email-verify ที่ **app.py **ได้แล้ว 😎 ลุย
from datetime import datetime, timedelta
from init_firebase import *
from fastapi import FastAPI, Response, Request
from models import User
from firebase_admin import auth
from rest_firebase_api import send_verify_email, sign_in_with_email_and_password
app = FastAPI()
@app.get("/")
def index():
return {"message": "Hello World"}
@app.post("/signup")
def sign_up(user: User):
email = user.email
password = user.password
user = auth.create_user(email=email, password=password)
return user
@app.post("/signin")
def sign_in(user: User, response: Response):
email = user.email
password = user.password
user = sign_in_with_email_and_password(email, password)
email_verified = auth.get_user_by_email(email).email_verified
user["email_verified"] = email_verified
if "idToken" in user:
response.set_cookie(
key="id_token",
value=user["idToken"],
expires=int((datetime.utcnow() + timedelta(hours=1)).timestamp()),
)
return user
@app.get("/send-email-verify")
def send_email_verify(request: Request):
id_token = request.cookies.get("id_token")
if id_token:
send_verify_email(id_token)
else:
return {"message": "please login first 😭"}
return {"message": "send email verify 😎"}
-
มาลองเทสกับ อันดับแรกอาจจะต้องใช้ อีเมลที่เราสามารถรับส่งที่ firebase จะส่งมาให้เราได้ หรืออาจจะใช้ Temp Mail ทดสอบก็ได้
-
ขั้นแรกต้องเริ่มตั้งแต่ต้น คือ signup นั ่นเอง เริ่มกัน ผมจะใช้ Temp Mail อันดับแรกไปเอามาก่อนที่ Temp Mail คัดลอกอีเมลมาโดยอีเมลมันจะสุ่มนะบอกไว้ก่อนอย่าใช้ตามผมเชื่อผม 😎 ใช้ของที่คุณได้ดีกว่า
- และเตรียม payload ไว้
{
"email":"cahinab325@fkcod.com",
"password":"12345678"
}
- เอาไปใส่แล้วยิงเส้น /signup ได้เลย
- และเราต้อง sign in ก่อนถึงจะใช้ เส้น /send-email-verify ได้เพราะไม่งั้น อาจจะเกิดอาการเศร้าได้นะ 😭 เช่นแบบนี้ เพราะผมดักไว้แล้ว 😁
- โอเคเอาจริงล่ะ เราต้อง sign in ก่อน เริ่มได้ payload เดิมเลย ยิงที่เส้น /signin กันลุย
{
"email":"cahinab325@fkcod.com",
"password":"12345678"
}
- แล้วต่อพระเอกของเราคือเส้น /send-email-verify 🥳 มาลองดูกัน อย่าลืมเปลี่ยน method เป็น GET นะ
- ต่อไปก็เช็คที่ Temp Email ที่เราได้มาได้เลยว่ามีอะไรส่งมาไหม?
- ทำการยืนยันอีเมลโดยคลิกลิงก์ได้เลย
- ก็จะได้หน้าตาแบบนี้นั่นเอง
- กลับตรวจสอบที่เส้น /signin อีกรอบว่า email_verified เปลี่ยน true หรือยัง ถ้าเรียบร้อยแสดงว่าใช้งานได้
- แน่นอนว่าข้อความที่ส่งไปในอีเมลของผู้ใช้นั้นดูไม่สวยเลย คุณสามารถแต่งได้นะโดยไปที่ firebase ที่ Authentication แล้วเลือกแท็บ Templates
- แก้ไขข้อความบางส่วนได้ไม่สามารถแก้ Message ได้เพราะ firebase บอกว่าเพื่อเป็นการป้องกันสแปม
- แต่คุณสามารถเปลี่ยนภาษาได้นะ โดยไปที่ Template language
คร่าวๆ ก็จะประมาณนี้ แต่ถ้าอยากไปต่อหล่ะก็ลิงก์ที่มันยาวๆ ตอนยืนยันอีเมล์ เราสามารถจัดการให้มันยิงตรงมายัง web server เราได้ 😎 เป็นยังไปดูส่วนถัดไปเลย
ปรับแต่ง URL ยืนยันอี เมล
- เราจะต้องทำการเขียนโค้ดกันต่อโดยที่ **rest_firebase_api.py **เราจะทำการเปลี่ยน url จาก https://fir-fastapi-33292.firebaseapp.com เป็นเส้น api ที่สร้างขึ้นเอง แต่ก่อนจะไปจุดนั้นต้องสร้างฟังก์ชันที่จะใช้ในการรองรับมันก่อนโดยการทำงานเดิมนั้น firebase จะส่งออกมาสองข้อมูล คือ mode=action&oobCode=code หรือก็คือ mode=verifyEmail&oobCode=IpA6RRNFU….. บลาๆ แต่สิ่งที่เราสนใจคือ oobCode เพราะงั้นเราจะทำการดึงข้อมูล oobCode ออกมาจาก url บ้านั่นกันและเอามาใช้กันในฟังก์ชันที่จะสร้างขึ้นชื่อว่า verify_email แน่นอนเตรียมลุยมาดูกันจะออกมาท่าวร ยุทธ์ไหน
from settings import FIREBASE_WEB_API_KEY
import requests
import json
SIGN_IN_WITH_PASSWORD_URL = (
"https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword"
)
SEND_VERIFY_EMAIL_URL = "https://identitytoolkit.googleapis.com/v1/accounts:sendOobCode"
VERIFY_EMAIL_URL = (
"https://www.googleapis.com/identitytoolkit/v3/relyingparty/setAccountInfo"
)
def sign_in_with_email_and_password(
email: str, password: str, return_secure_token: bool = True
):
payload = json.dumps(
{"email": email, "password": password, "returnSecureToken": return_secure_token}
)
r = requests.post(
SIGN_IN_WITH_PASSWORD_URL, params={"key": FIREBASE_WEB_API_KEY}, data=payload
)
return r.json()
def send_verify_email(id_token):
headers = {"Content-Type": "application/json"}
url = f"{SEND_VERIFY_EMAIL_URL}?key={FIREBASE_WEB_API_KEY}"
data = {"requestType": "VERIFY_EMAIL", "idToken": id_token}
response = requests.post(
url, headers=headers, json=data, auth=("api_key", FIREBASE_WEB_API_KEY)
)
return response
@app.get("/verify-email")
async def verify_email_link(request: Request):
oob_code = request.query_params.get("oobCode")
print(oob_code)
if oob_code:
response = verify_email(oob_code).json()
if "error" in response:
return {"message": response["error"]["message"]}
else:
return {"message": "please click link at email 😭"}
return {"message": "verify email 😎"}
- ม าสร้างเส้นที่จะให้ผู้ใช้งานเข้ามายืนยันกัน ขอใช้ชื่อว่า /verify-email แล้วกันง่ายดี มาลุยโค้ด จะมีการปรับขนาดใหญ่ เพราะผมลืม async 😅 จริงๆควรใส่ทุก ฟังก์ชันเลย เพราะว่าลืมแต่ครั้งนี้ใส่ให้ครบแล้ว กลับที่ **app.py **แล้วลุยเลย
from datetime import datetime, timedelta
from init_firebase import *
from fastapi import FastAPI, Response, Request
from starlette.requests import Request as StarletteRequest
from models import User
from firebase_admin import auth
from rest_firebase_api import (
send_verify_email,
sign_in_with_email_and_password,
verify_email,
)
app = FastAPI()
@app.get("/")
async def index():
return {"message": "Hello World"}
@app.post("/signup")
async def sign_up(user: User):
email = user.email
password = user.password
user = auth.create_user(email=email, password=password)
return user
@app.post("/signin")
async def sign_in(user: User, response: Response):
email = user.email
password = user.password
user = sign_in_with_email_and_password(email, password)
email_verified = auth.get_user_by_email(email).email_verified
user["email_verified"] = email_verified
if "idToken" in user:
response.set_cookie(
key="id_token",
value=user["idToken"],
expires=int((datetime.utcnow() + timedelta(hours=1)).timestamp()),
)
return user
@app.get("/send-email-verify")
async def send_email_verify(request: Request):
id_token = request.cookies.get("id_token")
if id_token:
send_verify_email(id_token)
else:
return {"message": "please login first 😭"}
return {"message": "send email verify 😎"}
@app.get("/verify-email")
async def verify_email_link(request: Request):
oob_code = request.query_params.get("oobCode")
print(oob_code)
if oob_code:
response = verify_email(oob_code).json()
if "error" in response:
return {"message": response["error"]["message"]}
else:
return {"message": "please click link at email 😭"}
return {"message": "verify email 😎"}
- เนื่องจากว่าเราเคยสมัครบัญชีนี้แล้ว ถ้าไม่อยากเปลืองอีเมลก็สามารถไปลบได้ที่ firebase เลยเหมือนเดิมแล้วก็ไปที่ Users แล้วเลือกผู้ใช้ที่จะลบ ถ้ายังไม่ขึ้นลองกดปุ่ม reset ข้างปุ่ม Add user ทางขวา
- ทำการเปลี่ยน url ที่จะถูกส่งไปยังอีเมลก่อน ไปที่ Templates แล้วเลือกรูปดินสอหรือแก้ไข
- เลือก Customize action URL แล้วก็ใส่ http://localhost:8000/verify-email หรือ http://127.0.0.1:8000/verify-email แล้วก็ Save เลย
- แล้วก็เทสเหมือนเดิมตั้งแต่แรกเลย ขอส่งเป็นรูปยาวๆนะจ๊ะ 😁
{
"email":"toxis52325@fkcod.com",
"password":"12345678"
}
ยอดเยี่ยมไปเลยใช่มั้ยการ Authentication ด้วย Firebase จริงๆมีอะไรที่อยากทำอีกเช่น Reset password , เปลี่ยนอีเมล , SMS verification และเชื่อมต่อฐานข้อมูลอย่าง MongoDB พวกนี้ แต่บทความนี้ยาวล่ะ ไม่รู้ว่าจะมีคนมาอ่านหรือเปล่า 😅 แต่ก็นั่นแหละใครที่มาอ่านถือว่าเรามีวาสนาต่อกันนะครับ ท้ายนี้ก็ขอบคุณที่อ่านบทความยันจบนะครับ เพราะว่าตอนแรกการใช้ Firebase สำหรับผมเป็นอะไรที่คิดว่ายากมากๆเลย แต่พอได้เริ่ม เขียนและลองศึกษาอย่างจริงจังล่ะ เขาเตรียทุกอย่างไว้ให้เยอะมากนะครับ ใครอยากดูท่าวรยุทธ์ต่างๆเพิ่มเติมไปดูได้ที่ https://github.com/firebase/firebase-admin-python/blob/master/integration/test_auth.py เขาเขียนไว้ให้แล้วเราแค่ดึงมาใช้ หวังว่าจะได้เจอกันอีกบทความหน้านะครับถ้าผม ขยัน 😁 ใครติดโค้ดตรงไหนรันไม่ได้ไปดูได้ที่นี่ครับ https://github.com/watchakorn-18k/fastapi-firebase-auth